package com.gitee.wsl.data.pool.sample

import com.gitee.wsl.data.pool.Pool
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.update

/**
 * A pool that produces at most one instance
 */
abstract class SingleInstancePool<T : Any> : Pool<T> {
    private val borrowed = atomic(0)
    private val disposed = atomic(false)

    private val instance = atomic<T?>(null)

    /**
     * Creates a new instance of [T]
     */
    protected abstract fun produceInstance(): T

    /**
     * Dispose [instance] and release its resources
     */
    protected abstract fun disposeInstance(instance: T)

    final override val capacity: Int get() = 1

    final override fun acquire(): T {
        borrowed.update {
            if (it != 0) error("Instance is already consumed")
            1
        }

        val instance = produceInstance()
        this.instance.value = instance

        return instance
    }

    final override fun release(instance: T): Boolean {
        if (this.instance.value !== instance) {
            if (this.instance.value == null && borrowed.value != 0) {
                error("Already recycled or an irrelevant instance tried to be recycled")
            }

            error("Unable to recycle irrelevant instance")
        }

        this.instance.value = null

        if (!disposed.compareAndSet(false, true)) {
            error("An instance is already disposed")
        }

        disposeInstance(instance)
        return true
    }

    final override fun dispose() {
        if (disposed.compareAndSet(false, true)) {
            val value = instance.value ?: return
            instance.value = null

            disposeInstance(value)
        }
    }
}

fun <T:Any> Pool.Companion.singleInstance(getBlock:() -> T,disposed:(T)->Unit = {}) = object:SingleInstancePool<T>(){

    override fun releaseAll(variables: List<T>) {}

    override fun produceInstance(): T = getBlock()

    override fun disposeInstance(instance: T) = disposed(instance)

}